//@version=6
// Revision 1.1: all standard-chart timeframes, bar-based calculation unchanged.
// Original experimental implementation. No Community code copied.
// BBWidth: https://www.tradingview.com/support/solutions/43000501972-bollinger-bandwidth-bbw/
// ATR: https://www.tradingview.com/support/solutions/43000501823-average-true-range-atr/
// Orders: https://www.tradingview.com/pine-script-docs/concepts/strategies/
strategy("[DAOL] Compression Release Strategy", overlay=true,pyramiding=0,
 initial_capital=1000000,currency=currency.THB,
 commission_type=strategy.commission.cash_per_contract,commission_value=50,
 slippage=1,margin_long=100,margin_short=100,calc_on_every_tick=false,
 calc_on_order_fills=false,process_orders_on_close=false,backtest_fill_limits_assumption=1,max_bars_back=500)
int qty=input.int(1,"Contracts",minval=1)
float targetR=input.float(2,"Target R",minval=0.5,step=0.1)
int lifetime=input.int(10,"Setup lifetime",minval=2,maxval=50)
int start=input.time(timestamp("01 Jan 2020 00:00 +0700"),"Test start")
int finish=input.time(timestamp("31 Dec 2099 00:00 +0700"),"Test end")
if barstate.isfirst
    if not chart.is_standard
        runtime.error("Use standard OHLC/candlestick charts.")
    if start>=finish
        runtime.error("Start must precede end.")
[basis,u,l]=ta.bb(close,20,2)
float bbw=basis!=0 ? 100*(u-l)/math.abs(basis) : na
float threshold=ta.percentile_linear_interpolation(bbw[1],100,20)
float atr=ta.atr(14)
float rh=ta.highest(high,10)
float rl=ta.lowest(low,10)
bool ready=bar_index>=120 and not na(threshold) and not na(atr) and atr>0
bool squeeze=ready and bbw<threshold
// On tick charts the realtime close timestamp can be unknown until bar closes.
// Decisions are confirmed-bar only; fall back to bar opening time if unavailable.
bool window=time>=start and (na(time_close) ? time<finish : time_close<finish)
var int streak=0
var bool fresh=true
var bool active=false
var int armedBar=na
var float top=na
var float bottom=na
var float frozenATR=na
var int direction=0
var int breakoutBar=na
var int submitted=na
var bool pending=false
var int heldDirection=0
var float stop=na
var float target=na
var string status="Building history"
bool buy=false
bool sell=false
bool closed=strategy.closedtrades>nz(strategy.closedtrades[1])
bool expiryBar=not na(submitted) and bar_index==submitted+1
if barstate.isconfirmed
    if not squeeze
        streak:=0
        fresh:=true
    else
        streak:=math.min(3,streak+1)
    if expiryBar
        pending:=false
        if strategy.position_size==0
            strategy.cancel_all()
            status:=closed ? "Trade completed" : "Unfilled limit expired"
    if not window
        active:=false
        streak:=0
        if strategy.position_size==0
            strategy.cancel_all()
        else
            strategy.close_all(comment="Test end")
        status:="Outside test window"
    else if strategy.position_size!=0
        active:=false
        bool returned=heldDirection==1 ? close<=top : close>=bottom
        if returned
            strategy.close_all(comment="Range return")
            status:="Range return exit requested"
        else
            status:="Holding release"
    else if not pending and not expiryBar and not closed and ready
        if active
            if bar_index-armedBar>lifetime
                active:=false
                direction:=0
                status:="Setup expired"
            else if direction==0
                if close>top or close<bottom
                    direction:=close>top ? 1 : -1
                    breakoutBar:=bar_index
                    status:="First breakout close"
            else if bar_index==breakoutBar+1
                float edge=direction==1 ? top : bottom
                float distance=direction*(close-edge)
                if distance>0 and distance<=0.5*frozenATR
                    stop:=direction==1 ? math.floor((edge-0.5*frozenATR)/syminfo.mintick)*syminfo.mintick : math.ceil((edge+0.5*frozenATR)/syminfo.mintick)*syminfo.mintick
                    float risk=direction*(close-stop)
                    float rawTarget=close+direction*risk*targetR
                    target:=direction==1 ? math.ceil(rawTarget/syminfo.mintick)*syminfo.mintick : math.floor(rawTarget/syminfo.mintick)*syminfo.mintick
                    heldDirection:=direction
                    submitted:=bar_index
                    pending:=true
                    if direction==1
                        strategy.entry("Long",strategy.long,qty=qty,limit=math.round_to_mintick(close),alert_message="Compression Long limit")
                        strategy.exit("Long bracket","Long",stop=stop,limit=target,comment_loss="Stop",comment_profit="Target")
                        buy:=true
                    else
                        strategy.entry("Short",strategy.short,qty=qty,limit=math.round_to_mintick(close),alert_message="Compression Short limit")
                        strategy.exit("Short bracket","Short",stop=stop,limit=target,comment_loss="Stop",comment_profit="Target")
                        sell:=true
                    status:="Limit submitted / valid next bar"
                else
                    status:="Canceled: no confirmation or chasing"
                active:=false
                direction:=0
        else if squeeze and streak>=3 and fresh and rh>rl
            top:=rh
            bottom:=rl
            frozenATR:=atr
            armedBar:=bar_index
            active:=true
            direction:=0
            fresh:=false
            status:="Frozen compression range"
plot(active or pending or strategy.position_size!=0 ? top : na,"Frozen high",color=color.blue,style=plot.style_linebr)
plot(active or pending or strategy.position_size!=0 ? bottom : na,"Frozen low",color=color.blue,style=plot.style_linebr)
plot(pending or strategy.position_size!=0 ? stop : na,"Fixed edge stop",color=color.red,style=plot.style_linebr)
plot(pending or strategy.position_size!=0 ? target : na,"Fixed target",color=color.green,style=plot.style_linebr)
plot(bbw,"BBWidth",display=display.data_window)
plot(threshold,"Prior100 percentile20",display=display.data_window)
plotshape(buy,title="Long submitted",style=shape.triangleup,location=location.belowbar,color=color.lime,text="L")
plotshape(sell,title="Short submitted",style=shape.triangledown,location=location.abovebar,color=color.red,text="S")
var table panel=table.new(position.top_right,1,3)
if barstate.islast
    table.cell(panel,0,0,"DAOL Compression Release",bgcolor=color.rgb(19,62,103),text_color=color.white)
    table.cell(panel,0,1,status)
    table.cell(panel,0,2,"Chart timeframe: " + timeframe.period + " / Calculation: bars")
// GUIDE: BB20/2 below percentile20 of PREVIOUS100 BBW for3 consecutive closes.
// Freeze high/low of10 bars including arm bar and ATR14 at arm. No arm-bar entry.
// First outside close followed by next close outside same edge; max0.5 frozenATR
// beyond edge. Both closes must occur within10 bars AFTER arm (deadline included).
// Failed confirmation/expiry consumes range. Need squeeze to end and fresh3-bar
// squeeze before another setup. No accumulation of setups while positioned.
// One limit entry at confirmation close, valid next complete bar only. May miss
// trades. Bracket Stop beyond frozen edge0.5ATR; Target2R from signal close.
// Fixed ABSOLUTE levels: better fill may improve R/R, gap through Stop may enter
// and immediately stop. Signals are submissions, not fills; no guaranteed R/R.
// Closed-bar return across breakout edge requests next-tick market exit while
// bracket remains live. No trailing, averaging, minimum holding or session exit.
// May hold overnight; intrabar stop/target, same-bar entry/exit possible.
// Any timeframe on standard charts: seconds/minutes/hours/days/weeks/months,
// and standard tick charts where available. No artificial chart types supported.
// All lengths remain BAR counts: BB20, reference100, range10, setup10, ATR14.
// Limit validity one bar can mean seconds, days or weeks depending on chart.
// Dates exclude bars closing at/after end; large bars may span date boundaries.
// Enough history required (at least121 bars); higher TFs may lack sample coverage.
// Timeframe compatibility is not performance validation. No HTF/future data.
// THB account/capital1m/commission50THB per contract per fill/slippage1tick/
// margin100% are TEST placeholders: set real Properties and verify pointvalue,
// currency (especially GO).100% margin can block trades for insufficient capital.
// Keep Every tick/After fill/Fill on close OFF. Qty input controls contracts.
// Last dataset bar may leave orders/trades unresolved. Dates do not load history.
// No profit or >60% win-rate claim. Numerical logic only; TV compile/backtest pending.
// Squeeze is established: extension is frozen setup, deadline, confirmation,
// max chase and range-return exit. No claim of worldwide uniqueness.
// Private tests first; English-first self-contained description, honest costs,
// sample size and limitations before public release; moderation not guaranteed.
// https://www.tradingview.com/support/solutions/43000590599/
// Educational simulation only, not investment advice/live order routing.
// ไทย: บีบตัว3แท่ง ตรึงกรอบ10แท่ง รอปิดหลุด2แท่ง แล้วLimitราคาแท่งยืนยัน
// Stopหลังขอบ0.5ATR เป้า2R คำสั่งอายุ1แท่ง อาจไม่ได้เข้า ราคากลับเข้ากรอบขอปิด
// ไม่บังคับถือขั้นต่ำ ตั้งต้นทุน/Marginตามจริง ยังไม่ยืนยันผลกำไรหรือWin Rate
